package com.hrpc.protection;

/**
 * 基于令牌桶算法的限流器
 */
public class TokenBuketRateLimiter implements RateLimiter{
    //1.代表令牌的数量，大于0说明有令牌，能放行，等于0说明无令牌，就阻拦
    private int tokens;
    //2.限流的本质，就是令牌数
    private final int capacity;
    //3.令牌桶的令牌如果没了，令牌本身按照一定的速率给令牌桶加令牌，如每秒加500个，不能超过总数
    //可以用定时任务去加--->启动一个定时任务，每秒执行一次让tokens加500，但是不能超过capacity(这个方案不好)
    //对于单机版的限流器，可以更简单：每一次我们有请求要发送的时候，给他加一下就好了，
    private final int rate;
    //上一次放令牌的时间
    private Long lastTokenTime;

    public TokenBuketRateLimiter(int capacity, int rate) {
        this.capacity = capacity;
        this.rate = rate;
        tokens=capacity;
        lastTokenTime=System.currentTimeMillis();
    }

    /**
     * 判断是否可以放行
     * @return true放行 false拦截
     */
    public synchronized boolean allowRequest(){
        //1.给令牌桶添加令牌
            //计算从现在到上一次的时间间隔需要添加的令牌数
        Long currentTime=System.currentTimeMillis();
        long timeInterval = currentTime-lastTokenTime;
        //如果间隔时间超过一秒，放令牌，同时标记最后时间
        if (timeInterval >= 1000/rate){
            int needAddTokens = (int) (timeInterval*rate/1000);
            //考虑容量问题
            tokens=Math.min(capacity,tokens+needAddTokens);
            //标记最后一次放入令牌的时间
            this.lastTokenTime=System.currentTimeMillis();
        }
        //2.自己获取令牌
        if (tokens > 0){
            System.out.println("TRUE");
            tokens--;
            return true;
        }else {
            System.out.println("FALSE");
            return false;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        TokenBuketRateLimiter tokenBuketRateLimiter=new TokenBuketRateLimiter(10,10);
        for (int i=0;i<100;i++){
            Thread.sleep(10);
            tokenBuketRateLimiter.allowRequest();
        }
    }
}
